package com.web.music.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.PersistenceContext;

import org.springframework.transaction.annotation.Transactional;

import com.web.music.interfaces.PersistenceHelper;
import com.web.music.model.PersistentEntity;
import com.web.music.model.PersistentEntityType;
import com.web.music.model.User;
import javax.persistence.Query;

/**
 * Persistence helper implementation.
 * For Javadocs please see PersistenceHalper interface.
 */

@Transactional
public class PersistenceHelperImpl implements PersistenceHelper {

	private EntityManager em;

	@PersistenceContext
	public void setEntityManager(EntityManager em) {
		this.em = em;
	}

	private EntityManager getEntityManager() {
		return em;
	}
	
	public void save(PersistentEntity object) {
		/*if (object.getId() == null) {
			getEntityManager().persist(object);
		} else {
			getEntityManager().merge(object);
		}*/
		getEntityManager().merge(object);
	}
	
	public void saveAll(List<? extends PersistentEntity> objects) {
		for (PersistentEntity object: objects){
			save(object);
		}
	}

	public PersistentEntity getById(Class<? extends PersistentEntity> clazz, Long id) {
		return getEntityManager().find(clazz, id);
	}
	
	@SuppressWarnings("unchecked")
	public List<PersistentEntity> findAll(Class<? extends PersistentEntity> clazz) {
		String entity = clazz.getSimpleName();
		return getEntityManager().createQuery("select e FROM " + entity + " e").getResultList();
	}

	public User findUserByLoginPassword(String login, String password) {
		Query findUserByLoginPassword = getEntityManager().createNamedQuery("User.findUserByLoginPassword");
		findUserByLoginPassword.setParameter("login", login);
		findUserByLoginPassword.setParameter("password", password);
		return (User) findUserByLoginPassword.getSingleResult();
	}
	
	
	public void delete(PersistentEntity object) {
		getEntityManager().remove(object);
	}
	
	public void delete(Class<? extends PersistentEntity> clazz, Long id) {
		delete(getById(clazz, id));
	}
	
	public void cleanupDB(){
		// delete everything from PERSON table
		List<PersistentEntity> objects = findAll(PersistentEntityType.PERSON.getObjectClass());
		for (PersistentEntity o: objects){
			delete(o);
		}
	}

	/**
	 * Checks (using reflection) if provided Class is annotated with @GeneratedValue
	 */
	public boolean isIdGenereated(Class<? extends PersistentEntity> clazz) {
		boolean debug = false;
		if (debug) System.out.println("\n[isIdGenerated] Starting to check class " + clazz.getCanonicalName());		
		Class<?> classToCheck = clazz;
		Field idField = null;
		boolean isSearchCompleted = false;
		while (!isSearchCompleted){
			try {
				idField = classToCheck.getDeclaredField("id");				
				isSearchCompleted = true;
			} catch (SecurityException e) {
				if (debug) System.out.println("[isIdGenerated] Exception while trying to retrieve 'id' field for class " + classToCheck.getCanonicalName());
				e.printStackTrace(System.out); 
				if (debug) System.out.println("[isIdGenerated] Returning FALSE");
				return false;
			} catch (NoSuchFieldException e) {
				if (debug) System.out.println("[isIdGenerated] No such field 'id' found for class " + classToCheck.getCanonicalName());
				if (classToCheck.getSuperclass() != null && !Object.class.equals(clazz.getSuperclass())){
					classToCheck = classToCheck.getSuperclass();
					if (debug) System.out.println("[isIdGenerated] Will proceed with checking super class " + classToCheck.getCanonicalName());
				}
				else{
					break;
				}
			}
		}
		
		if (idField == null){
			if (debug) System.out.println("[isIdGenerated] 'Id' field is not found. Returning FALSE");
			return false;
		}		
		if (debug) System.out.println("[isIdGenerated] Field 'id' is found. Now looking for annotations.");
		
		Annotation[] annotations = idField.getDeclaredAnnotations();
		for (Annotation a: annotations){
			if (GeneratedValue.class.equals(a.annotationType())){
				if (debug) System.out.println("[isIdGenerated] Annotation @GeneratedValue is found. Returning TRUE");
				return true;
			}
		}
		if (debug) System.out.println("[isIdGenerated] Annotation @GeneratedValue is not found. Returning FALSE");
		return false;	
	}
}
